﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Pinwheel.Toolkits;

namespace Pinwheel.LSerialize.Formatters
{
    [Preserve]
    public sealed unsafe partial class TypeFormatter : LFormatter<Type>
    {
        // Remove Version, Culture, PublicKeyToken from AssemblyQualifiedName.
        // Result will be "TypeName, Assembly"
        // see:http://msdn.microsoft.com/en-us/library/w3f99sx1.aspx
        
// #if NET7_0_OR_GREATER
//         [GeneratedRegex(@", Version=\d+.\d+.\d+.\d+, Culture=[\w-]+, PublicKeyToken=(?:null|[a-f0-9]{16})")]
//         private static partial Regex ShortTypeNameRegex();
// #else
        static readonly Regex _shortTypeNameRegex = new Regex(@", Version=\d+.\d+.\d+.\d+, Culture=[\w-]+, PublicKeyToken=(?:null|[a-f0-9]{16})", RegexOptions.Compiled);
        static Regex ShortTypeNameRegex() => _shortTypeNameRegex;
// #endif
        
        // private static ConcurrentDictionary<Type, string> m_TypeToName = new ConcurrentDictionary<Type, string>();
        // private static ConcurrentDictionary<string, Type> m_NameToType = new ConcurrentDictionary<string, Type>();
        
        [ThreadStatic]
        private static Dictionary<Type, string> m_TypeToName;
        [ThreadStatic]
        private static Dictionary<string, Type> m_NameToType;
        
        static TypeFormatter()
        {
            m_TypeToName = new Dictionary<Type, string>();
            m_NameToType = new Dictionary<string, Type>();
        }

        public static Dictionary<Type, string> TypeToName
        {
            get
            {
                if (null! == m_TypeToName)
                {
                    m_TypeToName = new Dictionary<Type, string>();
                }
                return m_TypeToName;
            }
        }
        
        public static Dictionary<string, Type> NameToType
        {
            get
            {
                if (null! == m_NameToType)
                {
                    m_NameToType = new Dictionary<string, Type>();
                }
                return m_NameToType;
            }
        }
        

        [Preserve]
        public override void Serialize(LSerializeContext* context, LStreamWriter* writer, ref Type value)
        {
            Profiler.Start("TypeFormatter.Serialize");
            if (!TypeToName.TryGetValue(value, out var shortName))
            {
                shortName = ShortTypeNameRegex().Replace(value.AssemblyQualifiedName, "");
                TypeToName.TryAdd(value, shortName);
                NameToType.TryAdd(shortName, value);
            }
            writer->WriteString(context, ref shortName);
            Profiler.End();
        }

        [Preserve]
        public override void Deserialize(LSerializeContext* context, LStreamReader* reader, ref Type value)
        {
            Profiler.Start("TypeFormatter.Deserialize");
            string typeName = null; 
            reader->ReadString(context, ref typeName);
            if (string.IsNullOrEmpty(typeName))
            {
                value = default;
            }
            else
            {
                if (!NameToType.TryGetValue(typeName, out value))
                {
                    value = Type.GetType(typeName, false);
                    if (null != value)
                    {
                        TypeToName.TryAdd(value, typeName);
                        NameToType.TryAdd(typeName, value); 
                    }
                } 
            }
            Profiler.End();
        }
    } 
}


